<template>
  <div>
    <el-upload
      v-model:file-list="fileList"
      :multiple="ismul"
      :limit="islim"
      :on-success="handleSuccess"
      :on-progress="handleProgress"
      :http-request="uploadChunk"
      :before-upload="handlebefore"
      :on-error="handleError"
      :on-exceed="handleExceed"
      class="upload-box"
    >
      <div class="upload-box-text">
        <el-icon size="45">
          <IconPark :icon="Upload" />
        </el-icon>
        <p>点击上传添加文件</p>
        <p>支持各类文档、图片、视频、压缩包格式</p>
      </div>
      <template #file="{ file }">
        <div class="file-box">
          <div class="file-box-left" @click="handlePreview(file)">
            <span>
              <img :src="fileSvg" />
            </span>
            <span>
              {{ file.name }}
              <div style="line-height: 20px">
                {{
                  Math.floor(file.size / 1024 / 1024) > 0
                    ? (file.size / 1024 / 1024).toFixed(2) + "M"
                    : (file.size / 1024).toFixed(2) + "KB"
                }}
              </div>
            </span>
          </div>
          <div class="file-box-right">
            <el-icon>
              <IconPark :icon="Close" @click="handleRemove(file)" />
            </el-icon>
          </div>
        </div>
      </template>
    </el-upload>
    <el-drawer
      v-model="isdrawer"
      direction="ltr"
      destroy-on-close
      size="100%"
      :show-close="previewtype !== 'el-image-viewer'"
    >
      <PreviewCompoonent
        :comtype="previewtype"
        :file="previewfile.raw"
        @closepreview="handleclose"
      />
    </el-drawer>
    <el-dialog
      v-model="dialogVisible"
      width="400"
      :style="dialogStyle"
      draggable
      :close-on-click-modal="false"
      :modal="false"
      :show-close="false"
    >
      <template #header>
        <span>正在上传，请稍后...</span>
      </template>
      <div class="dialog-content">
        <span>
          <el-icon size="60" color="#ff941f">
            <IconPark :icon="DocAdd" />
          </el-icon>
        </span>
        <div>{{ uploadfile.name }}</div>
        <div><el-progress :percentage="progressValue"></el-progress></div>
        <div>
          <span>{{ progressValue }}%</span> |
          <span>{{
            Math.floor(uploadfile.size / 1024 / 1024) > 0
              ? (uploadfile.size / 1024 / 1024).toFixed(2) + "M"
              : (uploadfile.size / 1024).toFixed(2) + "KB"
          }}</span>
        </div>
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import PreviewCompoonent from "@components/PreviewComponent/PreviewCompoonent.vue";
import type {
  UploadProps,
  UploadUserFile,
  UploadFile,
  UploadRequestOptions,
  UploadProgressEvent,
} from "element-plus";
import { ElMessage } from "element-plus";
import { Upload, DocAdd, Close } from "@icon-park/vue-next";
import IconPark from "@components/IconPark.vue";
import { CompType } from "@components/PreviewComponent/type";
import { ref, toRefs, watch } from "vue";
import { pull, find } from "lodash";
import { Tfile } from "@models/file";
import axios from "axios";
import fileSvg from "@assets/file.svg";
import SparkMd5 from "spark-md5";
const chunkSize = 10 * 1024 * 1024;

const props = defineProps<{
  fileIntrod: string;
  postId?: string;
  courseId?: string;
  ismul: boolean;
  islim: number;
}>();

const { fileIntrod, postId, courseId, ismul, islim } = toRefs(props);

const emit = defineEmits(["uploademit"]);

const fileList = ref<UploadUserFile[]>([]);

const isdrawer = ref<boolean>(false);
const dialogVisible = ref<boolean>(false);
const dialogStyle = {
  borderRadius: "8px",
  boxShadow: "none",
  border: "1px solid #dedede",
};

const progressValue = ref<number>(0);
const uploadfile = ref<UploadUserFile>();
const previewfile = ref<UploadFile>();
const previewtype = ref<CompType>();
const newfilelist = ref<Tfile[]>([]);

//计算文件大小
const calcSize = (file: File) => {
  return Math.floor(file.size / 1024 / 1024) > 0
    ? (file.size / 1024 / 1024).toFixed(2) + "M"
    : (file.size / 1024).toFixed(2) + "KB";
};

//关闭子组件的预览，关闭窗口
const handleclose = (value: boolean) => {
  isdrawer.value = value;
};

const handlebefore: UploadProps["beforeUpload"] = (rawFile) => {
  uploadfile.value = rawFile;
  dialogVisible.value = true;
};

const handleExceed: UploadProps["onExceed"] = (files) => {
  ElMessage.error(`不能超过文件限制数${files.length}`);
};

const handleSuccess: UploadProps["onSuccess"] = (response, upfile) => {
  dialogVisible.value = false;
  progressValue.value = 0;
  ElMessage({
    message: response.msg,
    type: "success",
  });
  newfilelist.value.push({
    name: upfile.name,
    type: response.type,
    fileMd5: response.md5,
    size: calcSize(upfile.raw),
    introduce: fileIntrod.value,
    postId: "0",
    courseId: courseId.value ?? "0",
  });
};

const handleError: UploadProps["onError"] = (error) => {
  dialogVisible.value = false;
  progressValue.value = 0;
  ElMessage.error(error);
};

const handleProgress: UploadProps["onProgress"] = (evt, uploadFile) => {
  progressValue.value = Math.round(evt.percent * 100);
  uploadFile.percentage = progressValue.value;
};

//手动去除文件列表的文件
const handleRemove = async (file) => {
  pull(fileList.value, file);
  const data = newfilelist.value.find((item) => {
    if ( item.name === file.name ) {
      return true;
    }
    return false;
  });
  const res = await axios({
    method: "delete",
    url: "/api/multipart/deleteFile",
    data: [data],
  });
  if (res.data.code === 200) {
    pull(newfilelist.value, data);
  }
};

const handlePreview: UploadProps["onPreview"] = (file) => {
  const type = file.raw.type;
  switch (type) {
    case "application/pdf":
      isdrawer.value = true;
      previewtype.value = "vue-office-pdf";
      previewfile.value = file;
      break;
    case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
      isdrawer.value = true;
      previewtype.value = "vue-office-docx";
      previewfile.value = file;
      break;
    case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
      isdrawer.value = true;
      previewtype.value = "vue-office-excel";
      previewfile.value = file;
      break;
    case (type.match(/^image/) || {}).input:
      //图片文件处理
      isdrawer.value = true;
      previewtype.value = "el-image-viewer";
      previewfile.value = file;
      break;
    default:
      ElMessage.error("暂不支持预览这个格式的文件！");
      break;
  }
};

async function getFileMd5(file, callback) {
  const totalChunks = Math.ceil(file.size / chunkSize);
  console.log("切片总数：" + totalChunks);
  const spark = new SparkMd5.ArrayBuffer();
  for (let i = 0; i < totalChunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(file.size, start + chunkSize);
    const arraybuffer = await file.slice(start, end).arrayBuffer();
    spark.append(arraybuffer);
  }
  callback(spark.end(), totalChunks);
}

function craeteFileChunk(file: File) {
  const fileChunkList = [];
  let count = 0;
  while (count < file.size) {
    fileChunkList.push({
      file: file.slice(count, count + chunkSize),
    });
    count += chunkSize;
  }
  return fileChunkList;
}

function getNewFileName(file: File, md5) {
  return md5 + "-" + file.name;
}

function checkChunkUploadProgress(
  totalChunk: number,
  successCount: number,
  options: UploadRequestOptions
) {
  return (p) => {
    options.onProgress({
      percent: parseFloat(
        ((successCount + p.progress) / totalChunk).toFixed(2)
      ),
    } as UploadProgressEvent);
  };
}

function uoloadChunkBase(
  chunkList: Array<any>,
  options: UploadRequestOptions,
  totalChunk: number,
  successCount: number
) {
  return new Promise((resolve) => {
    const handler = () => {
      if (chunkList.length) {
        const chunItem = chunkList.shift();
        axios
          .put(chunItem.uploadUrl, chunItem.chunk.file, {
            onUploadProgress: checkChunkUploadProgress(
              totalChunk,
              successCount,
              options
            ),
            headers: {
              "Content-Type": "application/octet-stream",
            },
          })
          .then((res) => {
            if (res.status == 200) {
              console.log("分片：" + chunItem.chunkNumber + " 上传成功");
              successCount++;
              handler();
            } else {
              console.log("上传失败：" + res.status + "," + res.statusText);
              resolve("fail");
            }
          })
          .catch((err) => {
            console.log("分片" + chunItem.chunkNumber + "上传失败" + err);
            chunkList.push(chunItem);
            handler();
          });
      } else {
        resolve("ok");
      }
    };
    handler();
  });
}

function mergeFile(param) {
  return new Promise(async (resolve) => {
    const res = await axios.post(`/api/multipart/merge`, param);
    if (res.data) {
      resolve(res.data.code);
    }
  });
}

const uploadChunk = (options: UploadRequestOptions) => {
  return new Promise(async (resolve, reject) => {
    const file = options.file;
    let ChunkList = [];
    getFileMd5(file, async (md5, totalChunk) => {
      //创建分片
      let fileChunks = craeteFileChunk(file);
      let filename = getNewFileName(file, md5);

      let param = {
        fileName: filename,
        fileSize: file.size,
        chunkSize,
        partCount: totalChunk,
        fileMd5: md5,
        contentType: "application/octet-stream",
        fileType: file.type,
      };
      
      const uploadIdInfoResult = await axios.post("/api/multipart/init", param);
      const uploadIdInfo = uploadIdInfoResult.data.data;
      const uploadUrls = uploadIdInfo.urlList;

      fileChunks.map((item, index) => {
        ChunkList.push({
          chunkNumber: index + 1,
          chunk: item,
          uploadUrl: uploadUrls[index],
          progress: 0,
          status: "-",
        });
      });

      const msg = await uoloadChunkBase(ChunkList, options, totalChunk, 0);
      if (msg === "ok") {
        if (uploadIdInfo.uploadId === "SingleFileUpload") {
          console.log("单文件上传！");
        } else {
          const mergeRes = await mergeFile({
            uploadId: uploadIdInfo.uploadId,
            fileName: filename,
            fileMd5: md5,
            fileType: file.type,
          });
          if (mergeRes === -1) {
            reject("文件上传失败！");
          }
        }
        resolve({ msg: "文件上传成功！", md5: md5, type: file.type });
      } else {
        reject("上传失败！");
      }
    });
  });
};

const uploadFileInfo = async () => {
  newfilelist.value[0].introduce = fileIntrod.value;
  const res = await axios({
    method: "post",
    url: "/api/multipart/uploadFileInfo",
    data: JSON.stringify(newfilelist.value),
    headers: {
      "Content-Type": "application/json",
    },
  });
  if (res.data.code === 1) {
    emit("uploademit", res.data.data);
  }
};

//设置帖子号
watch(
  () => postId.value,
  (newValue) => {
    newfilelist.value.forEach((item) => {
      item.postId = newValue;
    });
    uploadFileInfo();
  }
);

defineExpose({
  newfilelist,
  fileList,
  uploadFileInfo,
});
</script>

<style scoped lang="less">
@import "../../assets/css/uploadfile.less";
</style>
